package mynl

import (
	"fmt"
	"strings"
)

/////////////////////////////////////////////////////////////////////////
// 语义分析
// 对函数调用做引用消解（Refrence Resolving），也就是找到函数的声明。

/**
 * 遍历AST。如果发现函数调用，就去找它的定义。
 */
type RefResolver struct {
	AstVisitor
	prog *Prog
}

func NewRefResolver() *RefResolver {
	return &RefResolver{}
}

func (selfx *RefResolver) VisitProg(prog *Prog) {
	selfx.prog = prog

	for i := range prog.Stmts {
		//根据stmt的类型，进行不同的处理
		switch v := prog.Stmts[i].(type) {
		case *FunctionCall:
			selfx.resolveFunctionCall(prog, v)
		case *FunctionDecl:
			selfx.visitFunctionDecl(v)
		default:
		}
	}
}

//对于一个函数调用，查找函数声明，进行引用消解
func (selfx *RefResolver) resolveFunctionCall(prog *Prog, functionCall *FunctionCall) {
	var functionDecl = selfx.findFunctionDecl(prog, functionCall.Name)
	if functionDecl != nil {
		//非系统内置函数
		functionCall.Definition = functionDecl
		fmt.Println("resolveFunctionCall successed: " + functionCall.Name)
	} else {
		if !strings.HasPrefix(functionCall.Name, "nl.") {
			//除系统内置函数外，找不到引用，要报错
			fmt.Println("Error: cannot find definition of function " + functionCall.Name)
		}

		fmt.Println("resolveFunctionCall successed: " + functionCall.Name)
	}
}

//在全部stmts中，根据名称查找函数声明
func (selfx *RefResolver) findFunctionDecl(prog *Prog, name string) *FunctionDecl {
	for i := range prog.Stmts {
		switch v := prog.Stmts[i].(type) {
		case *FunctionDecl:
			if v.Name == name {
				return v
			}
		default:
		}
	}

	return nil
}

//通过函数声明，访问函数体
//这里要看子类如何继承
func (selfx *RefResolver) visitFunctionDecl(functionDecl *FunctionDecl) {
	selfx.visitFunctionBody(&functionDecl.Body)
}

//对函数体中的每一个函数调用，都进行引用消解
func (selfx *RefResolver) visitFunctionBody(functionBody *FunctionBody) {
	if selfx.prog != nil {
		for i := range functionBody.Stmts {
			selfx.resolveFunctionCall(selfx.prog, &functionBody.Stmts[i])
		}
	}
}
